package com.erlang4j.internal.process;

import static com.erlang4j.api.Erlang4jMessageLanguage.binding;
import junit.framework.Assert;
import junit.framework.TestCase;

import com.ericsson.otp.erlang.OtpErlangPid;
import com.erlang4j.api.IBinding;
import com.erlang4j.api.exceptions.Erlang4jCannotChangeStateToIllegalStateException;
import com.erlang4j.api.exceptions.Erlang4jCannotChangeStateTwiceException;
import com.erlang4j.api.exceptions.Erlang4jKeyNotFoundException;
import com.erlang4j.api.process.IProcess;
import com.erlang4j.internal.utilities.Erlang4jCollectionHelper;
import com.erlang4j.tests.Erlang4JTestHelper;

public class ProcessWrapperTest extends TestCase {

	private ProcessWrapper processWrapper;
	private MockProcess process1;

	public void testBindWithNewValuesIsFoundInNewMatchBindings() {
		processWrapper.bind("New", 2);
		assertEquals(1, processWrapper.getNewMatchingBinding().getInt("Match"));
		assertEquals(2, processWrapper.getNewMatchingBinding().getInt("New"));
	}

	public void testBindWithNewValuesCanChangeOriginalMatchBindings() {
		processWrapper.bind("Match", "newMatch");
		assertEquals("newMatch", processWrapper.getNewMatchingBinding().getString("Match"));
	}

	public void testBindWithNewValuesDoesntChangeStateNow() {
		processWrapper.bind("New", 2);
		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, new Runnable() {
			public void run() {
				processWrapper.matchingBinding().getString("New");
			}
		});
		Erlang4JTestHelper.assertThrows(Erlang4jKeyNotFoundException.class, new Runnable() {
			public void run() {
				process1.matchingBinding().getString("New");
			}
		});
		assertEquals(1, processWrapper.getNewMatchingBinding().getInt("Match"));
		assertEquals(2, processWrapper.getNewMatchingBinding().getInt("New"));
	}

	public void testCanChangeStateOnce() {
		assertNull(processWrapper.getNewStateName());
		processWrapper.changeState("legal1");
		Erlang4JTestHelper.assertThrows(Erlang4jCannotChangeStateTwiceException.class, "Cannot change state twice in a message handler. Old Value legal1 New value legal2", new Runnable() {
			public void run() {
				processWrapper.changeState("legal2");
			}
		});
		assertEquals("legal1", processWrapper.getNewStateName());
	}

	public void testChangeStateToIllegalValue() {
		Erlang4JTestHelper.assertThrows(Erlang4jCannotChangeStateToIllegalStateException.class, "Cannot change state to illegal as that is not in the list of legal values [legal2, legal1] ", new Runnable() {
			public void run() {
				processWrapper.changeState("illegal");
			}
		});
	}

	@Override
	protected void setUp() throws Exception {
		super.setUp();
		process1 = new MockProcess(Erlang4JTestHelper.makePid(), binding("Match", 1));
		processWrapper = new ProcessWrapper(process1, Erlang4jCollectionHelper.getSet("legal1", "legal2"));
	}
}

class MockProcess implements IProcess{
	public IBinding matchBinding;
	public OtpErlangPid self;

	public boolean terminate;

	public boolean alive;
	public Object[] sendData;
	public int sendCount;

	public MockProcess(OtpErlangPid self, IBinding matchBinding) {
		this.self = self;
		this.matchBinding = matchBinding;
	}

	@Override
	public IBinding matchingBinding() {
		return matchBinding;
	}

	// ============methods the ProcessWrapper cannot call: these change state ===============


	@Override
	public void bind(String key, Object object) {
		Assert.fail();
	}

	// ===To allow the threading aspect to be checked
	public void setIsAlive(boolean alive) {
		this.alive = alive;
	}

	@Override
	public boolean isAlive() {
		return alive;
	}

	@Override
	public void terminate() {
		this.terminate = true;
	}

	@Override
	public OtpErlangPid self() {
		return self;
	}

	// sending code

	@Override
	public void send(Object serverNameOrPid, String pattern, Object... parameters) {
		recordSend(serverNameOrPid, pattern, parameters);
	}

	@Override
	public void sendRaw(Object serverNameOrPid, Object object) {
		recordSend(serverNameOrPid, object);
	}

	@Override
	public void sendRpc(Object serverNameOrPid, String atom, Object... parameters) {
		recordSend(serverNameOrPid, atom, parameters);
	}

	@Override
	public void sendWithBinding(Object serverNameOrPid, String pattern, IBinding binding) {
		recordSend(serverNameOrPid, pattern, binding);
	}

	private void recordSend(Object... data) {
		sendCount++;
		this.sendData = data;

	}

}
